package com.yin.myproject.zk.recipes.lock;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @author Eason
 * @create 2018-04-12 11:17
 **/
public class ExclusiveLock implements Lock {
    private static final Logger logger = LoggerFactory.getLogger(ExclusiveLock.class);
    private static final String LOCK_NODE_FULL_PATH = "/exclusive_lock/lock";
    /**
     * 自旋测试超时阈值，考虑到网络的延时性，这里设为1000毫秒
     */
    private static final long spinForTimeoutThreshold = 1000L;
    private static final long SLEEP_TIME = 100L;
    private LockStatus lockStatus;
    private ZooKeeper zooKeeper;
    private String id = String.valueOf(new Random(System.nanoTime()).nextInt(10000000));
    private CyclicBarrier lockBarrier = new CyclicBarrier(2);

    public ExclusiveLock(String lockPath) {
        //建立连接
        zooKeeper = new ZookeeperUtils("192.168.212.104:2181", 50 * 1000).getZk();
        lockStatus = LockStatus.UNLOCK;
    }

    @Override
    public void lock() {
        //判断当前锁的状态
        if (lockStatus != LockStatus.UNLOCK) {
            return;
        }
        // 1. 创建锁节点
        if (createLockNode()) {
            System.out.println("[" + id  + "]" + " 获取锁");
            lockStatus = LockStatus.LOCKED;
            return;
        }
        lockStatus = LockStatus.TRY_LOCK;
        try {
            lockBarrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    private Boolean createLockNode() {
        try {
            zooKeeper.create(LOCK_NODE_FULL_PATH, "".getBytes(),
                    ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        } catch (KeeperException | InterruptedException e) {
            return false;
        }

        return true;
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        if (lockStatus == LockStatus.LOCKED) {
            return true;
        }
        Boolean created = createLockNode();
        lockStatus = created ? LockStatus.LOCKED : LockStatus.UNLOCK;
        return created;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        long millisTimeout = unit.SECONDS.toMillis(1);
        if (millisTimeout <= 0L) {
            return false;
        }
        final long deadline = System.currentTimeMillis() + millisTimeout;
        for (;;) {
            if (tryLock()) {
                return true;
            }
            if (millisTimeout > spinForTimeoutThreshold) {
                Thread.sleep(SLEEP_TIME);
            }
            millisTimeout = deadline - System.currentTimeMillis();
            if (millisTimeout <= 0L) {
                return false;
            }
        }
    }

    @Override
    public void unlock() {
        if(lockStatus == LockStatus.UNLOCK){
            return;
        }
        try {
            deleteLockNode();
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
        lockStatus = LockStatus.UNLOCK;
        lockBarrier.reset();
        System.out.println("[" + id + "]" + " 释放锁");
    }

    private void deleteLockNode() throws KeeperException, InterruptedException {
        Stat stat = zooKeeper.exists(LOCK_NODE_FULL_PATH, false);
        zooKeeper.delete(LOCK_NODE_FULL_PATH, stat.getVersion());
    }

    @Override
    public Condition newCondition() {
        return null;
    }

    enum LockStatus {
        TRY_LOCK,
        LOCKED,
        UNLOCK
    }
}
